/*
*********************************
	  SF2D Engine
*********************************

	By Julian Williams
	August 8, 2010
**********************************

	Description:

		Entity class that defines 
	all things within the game's 
	world.
***********************************
*/
#include <iostream>
#include <assert.h>
#include <Box2D/Box2D.h>


using namespace std;

#include "cfg_reader.h"
#include "image_manager.h"
#include "script_manager.h"
#include "app.h"
#include "path_finder.h"
#include "physics_world.h"
#include "helper.h"


#include "entity.h"
#include "creep.h"
#include "tower.h"
#include "projectile.h"






//a private copy function so that i don't have to
//copy/paste the copy constructor to the
//equal operator
void Entity::copyEntity(const Entity *e)
{
//General stuff
	//SetPosition(e.GetPosition());
	SetOrigin(e->GetOrigin());

	damage = e->damage;
	name = e->name;
	speed = e->speed;
	type = e->type;

//image stuff
	//img = e.img;
	tile_size = e->tile_size;
	//SetImage( *img );


//Script stuff
//	script_location = e.script_location;
	driverFunctionID = e->driverFunctionID;

//Physics stuff
	bodyDef = e->bodyDef;
	shapeDef = e->shapeDef;
	circleDef = e->circleDef;
	fixtureDef = e->fixtureDef;

	//body = e.body;

}//EoF



//Prepare Spawn
void Entity::PrepareSpawn(const b2Vec2& start)
{
	Set_State(ES_ACTIVE);
	//SetImage( *img );

	hasDriver = true;	
	


}//EoF

//Copy Constructor
Entity::Entity(const Entity &other)
{
	
	refCount = 1;

	Entity new_other;
	new_other = other;
	copyEntity(&new_other);
	
}//EoF


//Set Entity equal to each other
Entity& Entity::operator = ( const Entity& other )
{	
		//Entity new_other = other;
		copyEntity(&other);

        return *this;
}//eof


Entity::Entity()
{
	refCount = 1;
	hasDriver = true;
}

Entity::~Entity()
{
//	assert( refCount == 0 );
	//Damn it, delete crashes when its used in Entity_Factory's Load_IntoList function
	//Solve memory leak later...
	//cout << "Why is this being called?\n";
	//Destroy();
}

void Entity::Destroy()
{
	//safeDelete( shapeDef );
	//safeDelete( circleDef );
	physicsManager.world->DestroyBody( body );
	
	
}//EoF

//--------------------
// reference counting
//--------------------

void Entity::AddRef()
{
	refCount++;
}



void Entity::Release()
{
	if( --refCount == 0 )
		delete this;
}


//-----------------------
// AngelScript functions
//-----------------------

static void EntityDefaultFactory(Entity *self)
{
	new(self) Entity();
}


/***********************
 Angelscript Functions
 ***********************/
void Entity_ApplyForce(Entity *e, const b2Vec2 &impulse, const b2Vec2 &point)
{
	e->body->ApplyForce(impulse, point);
}//EoF

void Entity_ApplyLinearImpulse(Entity *e, const b2Vec2 &impulse, const b2Vec2 &point)
{
	e->body->ApplyLinearImpulse(impulse, point);
}//EoF

b2Vec2 Entity_GetLinearVelocity(Entity *e)
{
	return e->body->GetLinearVelocity();
}//EoF

b2Vec2 Entity_GetWorldPoint(Entity *e, const b2Vec2 &point)
{
	return e->body->GetWorldPoint(point);
}//EoF


b2Vec2 e_GetPosition(Entity *e)
{
	b2Vec2 pos = PIXELS_PER_METER * e->body->GetPosition();
	return pos;
}//EoF

//Register the Class interface
bool Entity::Register_With_ScriptEngine( const string &script_dir )
{
	
	PathComponent::Register_With_ScriptEngine(aScriptEngine.GetEngine());

	int r;

	// Register the type
	r = aScriptEngine.GetEngine()->RegisterObjectType("Entity", 0, asOBJ_REF); assert( r >= 0 );

	// Register the object operator overloads
	// Note: We don't have to register the destructor, since the object uses reference counting
	r = aScriptEngine.GetEngine()->RegisterObjectBehaviour("Entity", asBEHAVE_FACTORY, "Entity @f()", asFUNCTION(EntityDefaultFactory), asCALL_CDECL); assert( r >= 0 );
	//r = engine->RegisterObjectBehaviour("string", asBEHAVE_FACTORY,    "string @f(const string &in)", asFUNCTION(StringCopyFactory), asCALL_CDECL); assert( r >= 0 );
	r = aScriptEngine.GetEngine()->RegisterObjectBehaviour("Entity", asBEHAVE_ADDREF,  "void f()", asMETHOD(Entity, AddRef),  asCALL_THISCALL); assert( r >= 0 );
	r = aScriptEngine.GetEngine()->RegisterObjectBehaviour("Entity", asBEHAVE_RELEASE, "void f()", asMETHOD(Entity, Release), asCALL_THISCALL); assert( r >= 0 );


	// Register the Entity methods
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "vector2 GetPosition()", asFUNCTION(e_GetPosition), asCALL_CDECL_OBJFIRST);					assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void Destroy()", asMETHOD(Entity, MarkRemoval), asCALL_THISCALL);					assert( r >= 0 );
		
	//Probably get rid of all these, since derived classes basicly replace them anyway
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void Move(float, float)", asMETHODPR(Entity, Move,(float, float), void), asCALL_THISCALL);						assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void SetPosition(float, float)", asMETHODPR(Entity, SetPosition, (float, float), void), asCALL_THISCALL);		assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void SetRotation(float)", asMETHOD(Entity, SetRotation), asCALL_THISCALL);							assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void Rotate(float)", asMETHODPR(Entity, Rotate_Angle,(float), void), asCALL_THISCALL);							assert( r >= 0 );

	//Physics Operations! I'll add more as I need them
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void ApplyForce(const vector2&, const vector2&)", asFUNCTION(Entity_ApplyForce), asCALL_CDECL_OBJFIRST);	assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void ApplyLinearImpulse(const vector2&, const vector2&)", asFUNCTION(Entity_ApplyLinearImpulse), asCALL_CDECL_OBJFIRST);					assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "vector2 GetLinearVelocity()", asFUNCTION(Entity_GetLinearVelocity), asCALL_CDECL_OBJFIRST);					assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "vector2 GetWorldPoint(const vector2&)", asFUNCTION(Entity_GetWorldPoint), asCALL_CDECL_OBJFIRST);					assert( r >= 0 );

		//b2Body.ApplyForce
	//Register Properties
		//r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "PathComponent pathComponent",	offsetof(Entity, pathComponent));		assert( r >= 0 );
		
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "float velocity_x",		offsetof(Entity, velocity.x));		assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "float velocity_y",		offsetof(Entity, velocity.y));		assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "float position_x",		offsetof(Entity, GetPosition().x));	assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "float position_y",		offsetof(Entity, GetPosition().y));	assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "float angle",			offsetof(Entity, angle));			assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "int damage",			offsetof(Entity, damage));			assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "float speed",			offsetof(Entity, speed));			assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "int state",			offsetof(Entity, state));			assert( r >= 0 );

		/*
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "float spread_angle",	offsetof(Entity, spread_angle));		assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "int min_speed",			offsetof(Entity, min_speed));		assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "int max_speed",			offsetof(Entity, max_speed));		assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "int life",				offsetof(Entity, life));				assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "int rotation_amount",	offsetof(Entity, rotation_amount));	assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "int type",				offsetof(Entity, type));				assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "bool collision",		offsetof(Entity, collision));		assert( r >= 0 );
		*/
		//Register enums
		
		r = aScriptEngine.GetEngine()->RegisterEnum("Entity_State"); assert( r >= 0 );
		int enum_index = 0;
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Entity_State", "ES_NONE", 0 ); assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Entity_State", "ES_ACTIVE", 1 ); assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Entity_State", "ES_DEAD", 2 ); assert( r >= 0 );
		/*
		r = aScriptEngine.GetEngine()->RegisterEnum("Particle_Type"); assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Particle_Type", "PT_NONE", 0 ); assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Particle_Type", "PT_PROJECTILE", 1 ); assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Particle_Type", "PT_NADES", 2 ); assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Particle_Type", "PT_SLUGS", 3 ); assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterEnumValue("Particle_Type", "PT_MISSLES", 4 ); assert( r >= 0 );
		*/
		// TODO: Add factory  string(const string &in str, int repeatCount)

	// TODO: Add explicit type conversion via constructor and value cast

	// TODO: Add parseInt and parseDouble. Two versions, one without parameter, one with an outparm that returns the number of characters parsed.
		

//Register individual entities
	//Creeps
		if(!Creep::Register_With_ScriptEngine()) { return false; }
	//Towers
		if(!Tower::Register_With_ScriptEngine()) { return false; }
	//Projectiles
		if(!Projectile::Register_With_ScriptEngine()) { return false; }

		
		string script_location = script_dir + "entity_drivers.as";
		cout << "Loading Script File From: " << script_location << endl;

		// Load weapons script file
		if (aScriptEngine.AddSectionFromFile(script_location.c_str()) < 0)
		{
		//aScriptEngine.Destroy();
			cout << "Failed to load script file." << endl;
	//	hasDriver = false;

			return false;
		}
		
		if(r < 0)
		{
			cout << "Register Class Result: " << r << endl;
			return false;
		}

	return true;
}//EoF





int CallEntityFunction(asIScriptContext * Context, Entity &e)
{
		int r = -1;

			Context->Prepare( e.driverFunctionID );

			Context->SetArgObject(0, &e);

			r = Context->Execute();

		if( r != asEXECUTION_FINISHED )
		{
			// The execution didn't finish as we had planned. Determine why.
			if( r == asEXECUTION_ABORTED )
			{
				cout << "The script was aborted before it could finish. Probably it timed out." << endl;
			}
			else if( r == asEXECUTION_EXCEPTION )
			{
				cout << "The script ended with an exception." << endl;

				// Write some information about the script exception
				int funcID = Context->GetExceptionFunction();
				asIScriptFunction *func = aScriptEngine.GetEngine()->GetFunctionDescriptorById(funcID);
				cout << "func: " << func->GetDeclaration() << endl;
				cout << "modl: " << func->GetModuleName() << endl;
				cout << "sect: " << func->GetScriptSectionName() << endl;
				cout << "line: " << Context->GetExceptionLineNumber() << endl;
				cout << "desc: " << Context->GetExceptionString() << endl;
			}
			else
			{
				cout << "The script ended for some unforeseen reason (" << r << ")." << endl;
			}
	  }
		return r;
}//EoF





// run Scriptz!
bool Entity::RunScriptDriver( asIScriptContext * context)
{
	//Call its Script Driver
	if(!hasDriver)
	{
				//aScriptEngine.CallScriptFunction( ctxMain, p.DriverFunctionID );
			cout << Get_Name() << " - Doesn't have Script Driver ;O\n";
			return false;
	}

	CallEntityFunction( context, *this);

	return true;
}//EoF


//Render object to screen
void Entity::Render(sf::RenderWindow *rendow)
{
	//make sure its not dead
		if(Get_State() != ES_NONE)
		{
			//Draw ALL ov the items
			rendow->Draw(*this);
		}
}//EoF



//Update the Entity
void Entity::Update_Logic(  asIScriptContext * context )
{
			//Run Script Driverz
			RunScriptDriver(context);


			//Update Graphics to Physics' Engine's Positions
			sf::Vector2f real_positions;
			real_positions.x = body->GetPosition().x* PIXELS_PER_METER;
			real_positions.y = body->GetPosition().y* PIXELS_PER_METER;

			SetPosition( real_positions );

			//Do the same for angles eventually		
}//EoF



/* Get the Ammo Configuration file and load properties */
bool Entity::Get_Config(const string& resource_dir)
{

	
	//grab and create the nessisary directories
	string image_location = resource_dir + string("image.png");
	string cfg_location = resource_dir + string("properties.cfg");
	//string script_name = App::getInstance().Resources_Dir["scripts"];
	//string snd_location = folder_location + string("fire.wav");

	cout << "Loading Entity folder: " << resource_dir << endl;
	cout << "Config from: " << cfg_location << endl;
	cout << "Image from: " << image_location << endl;

	//cout << "Gun sound from: " << snd_location << endl;



//Now Grab the firing sound for the gun
	//if(!Load_Sounds(s_fire, SND_fire, snd_location)) { return false; }




//Grab particle Config
		cfg_reader cfg(cfg_location, true);


//Get all of the properties
			type = cfg.Get_Info("info:type", 1);
			name = cfg.Get_Info("info:name", "Default");
			
			damage = cfg.Get_Info("info:damage", 10);
			speed = cfg.Get_Info("info:speed", 100);

		//	script_name += cfg.Get_Info("info:script_driver", "NULL");


//Animation related information
			tile_size.x = cfg.Get_Info("animation:tile_width", 32);
			tile_size.y = cfg.Get_Info("animation:tile_height", 32);

			sf::Vector2f origin;
			origin.x = cfg.Get_Info("animation:origin_x",16);
			origin.y = cfg.Get_Info("animation:origin_y",16);
			SetOrigin( origin );
		
			//cout << "Particle Image from: " << image_location << "\n\n";

			//Load Particle Image used for each type
			img = gImageManager.getResource( image_location );
			if(img == NULL) { return false; }
			SetImage( *img );

//Setup Physics

			//bodyDef.position.Set(width/2*METERS_PER_PIXEL, height/2*METERS_PER_PIXEL);
//create the dynamic body
			if(speed > 15000)
			{
				bodyDef.bullet = true;
			}

			bodyDef.type = static_cast<b2BodyType> (cfg.Get_Info("physics:body_type", b2_dynamicBody));
			bodyDef.linearDamping = cfg.Get_Info("physics:linear_damping", 2);
			bodyDef.fixedRotation = cfg.Get_Info("physics:fixed_rotation", false);

			//shape of the dynamic body
			shapeDef.SetAsBox(tile_size.x/2*METERS_PER_PIXEL, tile_size.y/2*METERS_PER_PIXEL);
			
			//circle.m_p.Set(50*MPP, 50*MPP);
			circleDef.m_radius = ((tile_size.x-10)/2)*METERS_PER_PIXEL;
		

			fixtureDef.shape		= &circleDef;
			fixtureDef.density		= cfg.Get_Info("physics:density", 1.0);
			fixtureDef.friction		= cfg.Get_Info("physics:friction", 0.3); 
			fixtureDef.restitution	= cfg.Get_Info("physics:bounciness", 0.5);
			fixtureDef.isSensor		= cfg.Get_Info("physics:is_sensor", false);

			//apply the fixture to the body
			//body->CreateFixture(&fixtureDef);

	return true;
}//EoF




//Mark the Object to be removed (just sets its state to NONE)
//Used for Scripting convienence
void Entity::MarkRemoval()
{
	Set_State(ES_NONE);
}//EoF